package snapex.expense.controller;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import snapex.core.ValidationFailureException;
import snapex.core.model.Expense;
import snapex.core.model.ExpenseDetail;
import snapex.core.model.Result;
import snapex.core.model.SearchParams;
import snapex.expense.service.IExpenseService;

@Slf4j
@RestController
public class ExpenseController{
		
	@Autowired
	private IExpenseService expenseService;
	
	@ApiOperation(value="Retrieve expense basic information")
	@ApiImplicitParam(name = "expenseId", value = "expenseId e.g. \"E0000001\"", required = true, dataType = "String")
	@GetMapping(value = "/expense/{expenseId}/basic", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Read')")
	public ResponseEntity<Result<Expense>> getExpense(@PathVariable String expenseId) {	
				
		return ResponseEntity.ok(expenseService.getExpense(expenseId, true));	
	}
	
	@ApiOperation(value="Retrieve expense basic information, expense purposes and deductions")
	@ApiImplicitParam(name = "expenseId", value = "expenseId e.g. \"E0000001\"", required = true, dataType = "String")
	@GetMapping(value = "/expense/{expenseId}/detail", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Read')")
	public ResponseEntity<Result<Expense>> getExpenseDetail(@PathVariable String expenseId) {				
		return ResponseEntity.ok(expenseService.getExpense(expenseId, false));	
	}
	
	@ApiOperation(value="Search a list of expenses")	
	@PostMapping(value = "/expense/search", consumes=MediaType.APPLICATION_JSON_UTF8_VALUE, produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Read')")
	public ResponseEntity<Result> searchExpenses(@RequestBody SearchParams condition) {
		condition.setUsername(username());
		return ResponseEntity.ok(expenseService.getExpenses(condition));	
	}
	
	@ApiOperation(value="Save draft version of expense, some fields will be validated")
	@ApiImplicitParam(name = "expense", value = "expense information", required = true, dataType = "Expense")
	@PostMapping(value = "/expense/savedraft", consumes=MediaType.APPLICATION_JSON_UTF8_VALUE, produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Create')")
	public ResponseEntity<Result> saveDraftExpense(@RequestBody @Validated Expense expense, BindingResult bindingResult) {
		if (bindingResult.hasErrors()){
			throw new ValidationFailureException(bindingResult);
        }
		expense.setUsername(username());
		return ResponseEntity.ok(expenseService.createExpense(expense));			
	}
	
	@ApiOperation(value="Save final version of expense, all fields will be validated")
	@ApiImplicitParam(name = "expense", value = "expense information", required = true, dataType = "Expense")
	@PostMapping(value = "/expense/savefinal", consumes=MediaType.APPLICATION_JSON_UTF8_VALUE, produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Create')")
	public ResponseEntity<Result> saveFinalExpense(@RequestBody @Validated Expense expense, BindingResult bindingResult) {
		
		if (bindingResult.hasErrors()){
			throw new ValidationFailureException(bindingResult);
        }
		expense.setUsername(username());
		return ResponseEntity.ok(expenseService.createExpense(expense));			
	}
	
	
	@ApiOperation(value="Delete draft expense")
	@ApiImplicitParam(name = "expenseId", value = "expense id", required = true, dataType = "String")
	@DeleteMapping(value = "/expense/{expenseId}/delete", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Create')")
	public ResponseEntity<Result> deleteExpense(@PathVariable String expenseId) {
					
		return ResponseEntity.ok(expenseService.deleteExpense(expenseId));		
	}
	
	@ApiOperation(value="Submit expense for approval")
	@ApiImplicitParam(name = "expenseId", value = "expense id", required = true, dataType = "String")
	@PostMapping(value = "/expense/{expenseId}/submit", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Create')")
	public ResponseEntity<Result> submitExpense(@PathVariable String expenseId) {
		
		return ResponseEntity.ok(expenseService.submitExpense(expenseId));		
	}	


	@ApiOperation(value="Retrieve a list of expense purposes")
	@ApiImplicitParam(name = "expenseId", value = "expenseId e.g. E0000001", required = true, dataType = "String")
	@GetMapping(value = "/expense/{expenseId}/purposes", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Read')")
	public ResponseEntity<Result> getExpensePurposes(@PathVariable String expenseId) {				
		return ResponseEntity.ok(expenseService.getExpensePurposes(expenseId));	
	}

	@ApiOperation(value="Retrieve a specific expense purpose")
	@ApiImplicitParam(name = "expensePurposeId", value = "expensePurposeId e.g. ED0000001", required = true, dataType = "String")
	@GetMapping(value = "/expense/purpose/{expensePurposeId}", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	public ResponseEntity<Result> getExpensePurpose(@PathVariable String expensePurposeId) {				
		return ResponseEntity.ok(expenseService.getExpensePurpose(expensePurposeId));
	}
	
	@ApiOperation(value="Create an expense purpose")
	@ApiImplicitParam(name = "purpose", value = "expense purpose detail", required = true, dataType = "ExpenseDetail")
	@PostMapping(value = "/expense/purpose/add", consumes=MediaType.APPLICATION_JSON_UTF8_VALUE, produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Create')")
	public ResponseEntity<Result> addExpensePurpose(@RequestBody @Valid ExpenseDetail purpose, BindingResult bindingResult) {
				
		if (bindingResult.hasErrors()){
			throw new ValidationFailureException(bindingResult);
        }
		
		return ResponseEntity.ok(expenseService.createExpensePurpose(purpose));			
	}
		
	@ApiOperation(value="Delete an expense purpose")
	@ApiImplicitParam(name = "expensePurposeId", value = "expense purpose id, e.g. ED0000001", required = true, dataType = "String")
	@PostMapping(value = "/expense/purpose/{expensePurposeId}/delete", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
	@PreAuthorize("hasAuthority('R_O_EXP_Create')")
	public ResponseEntity<Result> deleteExpensePurpose(@PathVariable String expensePurposeId) {
		
		return ResponseEntity.ok(expenseService.deleteExpensePurpose(expensePurposeId));			
	}
	
//	public ResponseEntity<Result> uploadExpensePurpose(@PathVariable String expensePurposeId, @RequestParam("invoice") MultipartFile invoice) throws IOException {
//        
//        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getServletContext());
//
//        if (multipartResolver.isMultipart(request)) {
//            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
//
//            Iterator<String> names = multiRequest.getFileNames();
//
//            while (names.hasNext()) {
//                MultipartFile file = multiRequest.getFile(names.next().toString());
//                if (file != null) {
//                    File appFile = new File(appRootDir, file.getOriginalFilename());
//                    file.transferTo(appFile);
//                }
//            }
//        }
//        
//		return ResponseEntity.ok(Result.success());			
//	}

	
	private String username() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();	
		return authentication.getName();
	}
}
